# ------ qt_app_ui_pur.py ------
# 追捕游戏监控UI
import sys
import os
import math
import time
import random
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 
                             QTableWidget, QTableWidgetItem, QLabel, QHeaderView, QSplitter,
                             QPushButton, QStatusBar, QGroupBox, QGridLayout)
from PyQt5.QtGui import QPixmap, QImage, QPainter, QPen, QColor, QBrush, QFont, QIcon
from PyQt5.QtCore import Qt, QTimer, QPointF, QRectF

class GraphMapWidget(QWidget):
    def __init__(self, graph_manager=None, parent=None):
        super().__init__(parent)
        self.graph = graph_manager
        self.robots = []
        self.scale_factor = 1.0
        self.offset_x = 0
        self.offset_y = 0
        self.capture_count = 0
        # 定义角色颜色
        self.role_colors = {
            "evader": QColor(255, 0, 0),     # 红色 - 逃避者
            "pursuer": QColor(0, 0, 255),    # 蓝色 - 追捕者
        }
        self.setMinimumSize(800, 600)
        self.setMouseTracking(True)
        # 如果没有提供图管理器，创建模拟数据
        if self.graph is None:
            self.simulate_graph()

    def simulate_graph(self):
        """模拟图数据（在没有真实数据的情况下）"""
        self.graph = type('', (), {})()  # 创建空对象
        self.graph.node_coordinates = []
        self.graph.adj_matrix = []
        # 生成模拟节点
        num_nodes = 20
        for i in range(num_nodes):
            angle = 2 * math.pi * i / num_nodes
            radius = 200 + random.randint(-50, 50)
            x = 300 + radius * math.cos(angle)
            y = 300 + radius * math.sin(angle)
            self.graph.node_coordinates.append((x, y))
        # 创建模拟邻接矩阵
        self.graph.adj_matrix = [[0] * num_nodes for _ in range(num_nodes)]
        for i in range(num_nodes):
            for j in range(i+1, num_nodes):
                if random.random() < 0.15:  # 15%的概率连接
                    self.graph.adj_matrix[i][j] = 1
                    self.graph.adj_matrix[j][i] = 1

    def update_robots(self, robots, capture_count=0):
        """更新机器人状态"""
        self.robots = robots
        self.capture_count = capture_count
        self.update()

    def world_to_pixel(self, x, y):
        """将世界坐标转换为像素坐标"""
        # 如果没有真实坐标，直接返回
        if not hasattr(self.graph, 'node_coordinates') or not self.graph.node_coordinates:
            return int(x), int(y)
        # 计算缩放和平移后的位置
        scaled_x = int(x * self.scale_factor + self.offset_x)
        scaled_y = int(y * self.scale_factor + self.offset_y)
        # 反转Y轴：将数学坐标系转换为屏幕坐标系
        flipped_y = self.height() - scaled_y
        return scaled_x, flipped_y

    def paintEvent(self, event):
        """绘制地图和机器人"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        # 绘制背景
        painter.fillRect(self.rect(), QColor(240, 240, 240))
        # 如果没有图数据，显示提示
        if not hasattr(self.graph, 'node_coordinates') or not self.graph.node_coordinates:
            painter.setPen(QPen(Qt.black))
            painter.setFont(QFont("Arial", 12))
            painter.drawText(self.rect(), Qt.AlignCenter, "没有地图数据可用")
            return
        # 绘制节点和边
        node_radius = 4
        for i, (x, y) in enumerate(self.graph.node_coordinates):
            px, py = self.world_to_pixel(x, y)
            # 绘制边
            for j in range(i+1, len(self.graph.node_coordinates)):
                if hasattr(self.graph, 'adj_matrix') and i < len(self.graph.adj_matrix) and j < len(self.graph.adj_matrix[i]):
                    if self.graph.adj_matrix[i][j] == 1:
                        jx, jy = self.graph.node_coordinates[j]
                        pjx, pjy = self.world_to_pixel(jx, jy)
                        painter.setPen(QPen(QColor(180, 180, 180), 1))
                        painter.drawLine(px, py, pjx, pjy)
        # 绘制节点
        for i, (x, y) in enumerate(self.graph.node_coordinates):
            px, py = self.world_to_pixel(x, y)
            # 绘制节点
            painter.setPen(QPen(QColor(80, 80, 80), 1))
            painter.setBrush(QBrush(QColor(220, 220, 220)))
            painter.drawEllipse(px-node_radius, py-node_radius, node_radius*2, node_radius*2)
            # 绘制节点编号
            painter.setPen(QPen(Qt.black))
            painter.setFont(QFont("Arial", 8))
            # painter.drawText(px+10, py+5, f"{i}")
        # 绘制机器人
        for robot in self.robots:
            if 'position' not in robot or robot['position'] is None:
                continue
            # 解析位置字符串 "x, y"
            try:
                pos_str = robot['position'].strip('()')
                x, y = map(float, pos_str.split(','))
            except:
                continue
            px, py = self.world_to_pixel(x, y)
            # 根据角色选择颜色
            role = robot.get('role', 'unknown')
            color = self.role_colors.get(role, QColor(128, 128, 128))
            # 绘制机器人位置
            robot_radius = 12
            painter.setPen(QPen(color.darker(), 2))
            painter.setBrush(QBrush(color.lighter(150)))
            painter.drawEllipse(px-robot_radius, py-robot_radius, 
                               robot_radius*2, robot_radius*2)
            # 绘制机器人ID和角色
            painter.setPen(QPen(Qt.black))
            painter.setFont(QFont("Arial", 9, QFont.Bold))
            role_symbol = "E" if role == "evader" else "P" if role == "pursuer" else "?"
            painter.drawText(px-15, py-15, f"{robot['id']}{role_symbol}")
            # 绘制目标位置（如果有）
            if 'target' in robot and robot['target'] != "未设置":
                try:
                    target_str = robot['target'].strip('()')
                    tx, ty = map(float, target_str.split(','))
                    tpx, tpy = self.world_to_pixel(tx, ty)
                    # 绘制目标点
                    painter.setPen(QPen(color, 2, Qt.DashLine))
                    painter.drawLine(px, py, tpx, tpy)
                    painter.setPen(QPen(color.darker(), 2))
                    painter.setBrush(QBrush(Qt.NoBrush))
                    painter.drawEllipse(tpx-6, tpy-6, 12, 12)
                except:
                    pass
            # 绘制状态指示器
            status = robot.get('status', 'unknown')
            if status == "active":
                status_color = Qt.green
            elif status == "connecting":
                status_color = Qt.yellow
            elif status == "disconnected":
                status_color = Qt.red
            else:
                status_color = Qt.gray
            painter.setPen(QPen(status_color, 2))
            painter.drawEllipse(px-robot_radius-2, py-robot_radius-2, 
                               (robot_radius+2)*2, (robot_radius+2)*2)
        # 绘制标题和捕获次数
        painter.setPen(QPen(Qt.black))
        painter.setFont(QFont("Arial", 14, QFont.Bold))
        painter.drawText(20, 30, "追捕游戏 - 实时监控")
        painter.setFont(QFont("Arial", 12))
        painter.drawText(20, 60, f"捕获次数: {self.capture_count}")

    def resizeEvent(self, event):
        """窗口大小改变时重新计算缩放因子"""
        super().resizeEvent(event)
        # 如果没有节点数据，不需要计算
        if not hasattr(self.graph, 'node_coordinates') or not self.graph.node_coordinates:
            return
        # 找到节点的边界
        min_x = min(x for x, y in self.graph.node_coordinates)
        max_x = max(x for x, y in self.graph.node_coordinates)
        min_y = min(y for x, y in self.graph.node_coordinates)
        max_y = max(y for x, y in self.graph.node_coordinates)
        # 计算缩放因子
        width_ratio = (self.width() - 100) / (max_x - min_x) if max_x - min_x > 0 else 1
        height_ratio = (self.height() - 100) / (max_y - min_y) if max_y - min_y > 0 else 1
        self.scale_factor = min(width_ratio, height_ratio)
        # 计算偏移量以使图形居中
        self.offset_x = (self.width() - (max_x - min_x) * self.scale_factor) / 2 - min_x * self.scale_factor
        self.offset_y = (self.height() - (max_y - min_y) * self.scale_factor) / 2 - min_y * self.scale_factor

class RobotInfoTable(QTableWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        # 修改列数和表头
        self.setColumnCount(5)
        self.setHorizontalHeaderLabels(["Robot Name", "当前位置", "目标位置", "最后更新", "离线状态"])
        
        # 设置表格样式
        self.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
        self.verticalHeader().setVisible(False)
        self.setSelectionBehavior(QTableWidget.SelectRows)
        self.setEditTriggers(QTableWidget.NoEditTriggers)
        
        # 设置列宽 (可以根据内容调整)
        self.setColumnWidth(0, 100)  # Robot Name
        self.setColumnWidth(1, 150)  # 当前位置
        self.setColumnWidth(2, 150)  # 目标位置
        self.setColumnWidth(3, 100)  # 最后更新
        self.setColumnWidth(4, 100)  # 离线状态
        
        self.setStyleSheet("""
            QTableWidget {
                font-size: 11px;
                gridline-color: #e0e0e0;
            }
            QHeaderView::section {
                background-color: #f0f0f0;
                padding: 4px;
                border: 1px solid #d0d0d0;
                font-weight: bold;
            }
        """)

    def update_robots(self, robots):
        """更新机器人信息表格 (根据新要求优化)"""
        self.setRowCount(len(robots))
        for row, robot in enumerate(robots):
            robot_name = robot.get('name', f"Robot {row+1}")
            name_item = QTableWidgetItem(robot_name)
            name_item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)
            self.setItem(row, 0, name_item)

            # 当前位置
            pos_item = QTableWidgetItem(robot.get('position', '未知'))
            self.setItem(row, 1, pos_item)

            # 目标位置
            target_item = QTableWidgetItem(robot.get('target', '未设置'))
            self.setItem(row, 2, target_item)

            # 最后更新时间 (格式化为秒数)
            last_update_secs = robot.get('last_update', 0)
            last_update_str = f"{last_update_secs:.1f}秒前"
            update_item = QTableWidgetItem(last_update_str)
            update_item.setTextAlignment(Qt.AlignCenter)
            # 根据更新时间设置颜色 (可选)
            if last_update_secs > 10:
                update_item.setForeground(QColor(200, 0, 0)) # 红色
            elif last_update_secs > 5:
                update_item.setForeground(QColor(200, 100, 0)) # 橙色
            self.setItem(row, 3, update_item)

            # 离线状态 (基于 'status' 字段)
            status = robot.get('status', '未知')
            offline_status = "在线" if status == "active" else "离线"
            offline_item = QTableWidgetItem(offline_status)
            offline_item.setTextAlignment(Qt.AlignCenter)
            # 根据状态设置颜色
            if status == "active":
                offline_item.setForeground(QColor(0, 128, 0)) # 绿色
            else: # connecting, disconnected, unknown
                offline_item.setForeground(QColor(200, 0, 0)) # 红色
            self.setItem(row, 4, offline_item)

            # 设置行背景色 (可选，根据状态区分)
            if status == "disconnected":
                for col in range(5): # 修改为5列
                    item = self.item(row, col)
                    if item:
                        item.setBackground(QColor(245, 245, 245)) # 浅灰色

# --- 简化或移除 ControlPanel，因为游戏逻辑由服务器控制 ---
class ControlPanel(QGroupBox):
    def __init__(self, parent=None):
        super().__init__("游戏状态", parent)
        self.setStyleSheet("""
            QGroupBox {
                font-weight: bold;
                border: 1px solid gray;
                border-radius: 5px;
                margin-top: 0.5em;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 3px 0 3px;
            }
        """)
        layout = QVBoxLayout() # 使用垂直布局
        # 游戏状态标签
        self.status_label = QLabel("游戏状态: 等待连接")
        self.status_label.setStyleSheet("font-size: 12px; font-weight: bold;")
        layout.addWidget(self.status_label)
        # 捕获次数标签
        self.capture_label = QLabel("捕获次数: 0")
        self.capture_label.setStyleSheet("font-size: 12px;")
        layout.addWidget(self.capture_label)
        # 预测步长标签 (如果需要显示)
        # self.predict_label = QLabel("预测步长: 0")
        # self.predict_label.setStyleSheet("font-size: 12px;")
        # layout.addWidget(self.predict_label)
        
        self.setLayout(layout)

    def update_status(self, game_active, capture_count):
        """更新控制面板状态"""
        self.capture_label.setText(f"捕获次数: {capture_count}")
        # self.predict_label.setText(f"预测步长: {predict_step}") # 如果需要显示预测步长
        if game_active:
            self.status_label.setText("游戏状态: <font color='green'>进行中</font>")
        else:
            self.status_label.setText("游戏状态: <font color='orange'>暂停</font>")

class PursuitGameUI(QMainWindow):
    def __init__(self, server=None, logger=None):
        super().__init__()
        self.server = server
        self.setWindowTitle("追捕游戏监控系统")
        self.setGeometry(100, 100, 1200, 800)
        self.logger = logger
        # 创建主布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        # 创建顶部状态栏
        self.status_bar = self.statusBar()
        self.status_label = QLabel("就绪")
        self.status_bar.addWidget(self.status_label)
        # 创建主分割器
        main_splitter = QSplitter(Qt.Horizontal)
        # 左侧：地图和控制面板
        left_widget = QWidget()
        left_layout = QVBoxLayout(left_widget)
        # 地图组件
        graph = server.graph if server else None
        self.map_widget = GraphMapWidget(graph)
        left_layout.addWidget(self.map_widget, 3)  # 3/4的空间
        # 控制面板
        self.control_panel = ControlPanel()
        left_layout.addWidget(self.control_panel, 1)  # 1/4的空间
        # 右侧：机器人信息表格
        self.robot_table = RobotInfoTable()
        # 添加到分割器
        main_splitter.addWidget(left_widget)
        main_splitter.addWidget(self.robot_table)
        main_splitter.setSizes([800, 400]) # 初始大小分配
        main_layout.addWidget(main_splitter)
        # 更新定时器
        self.update_timer = QTimer(self)
        self.update_timer.timeout.connect(self.update_display)
        self.update_timer.start(500)  # 每秒更新一次

    def update_display(self):
        """更新UI显示"""
        try:
            # 从服务器获取机器人信息和游戏状态
            robots = []
            game_active = False
            capture_count = 0
            # predict_step = 0 # 如果需要显示预测步长

            if self.server:
                # 从服务器获取所有机器人信息
                robots = self.server.get_all_robots_info() # 使用服务器提供的方法
                # 获取游戏状态
                game_active = self.server.game_active
                capture_count = self.server.game_strategy.capture_count
                # predict_step = self.server.game_strategy.predict_step # 如果需要显示预测步长
            else:
                # 模拟数据更新 (如果需要)
                # self.update_simulated_robots()
                # robots = self.sim_robots
                # game_active = True
                # capture_count = self.sim_capture_count
                pass # 如果没有服务器且不模拟，保持空列表

            # 更新地图
            self.map_widget.update_robots(robots, capture_count)
            # 更新表格 (使用优化后的表格)
            self.robot_table.update_robots(robots)
            # 更新控制面板
            self.control_panel.update_status(game_active, capture_count) # , predict_step # 如果需要传递预测步长
            # 更新状态栏
            online_count = sum(1 for r in robots if r['status'] == 'active')
            self.status_label.setText(
                f"机器人总数: {len(robots)} | "
                f"在线: {online_count} | "
                f"上次更新: {time.strftime('%H:%M:%S')}"
            )
        except Exception as e:
            if self.logger:
                self.logger.error(f"更新显示错误: {e}")
            else:
                print(f"更新显示错误: {e}")
